home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Think Class Libraries / CMIDI 2.2 / Lefty.ƒ / Lefty.cp < prev    next >
Encoding:
Text File  |  1994-11-30  |  10.5 KB  |  352 lines  |  [TEXT/KAHL]

  1. /*
  2.  *——— Lefty.cp ——————————————————————————————————————————————————————————
  3.  * Copyright © Paul D. Ferguson, 1990-94.  All rights reserved.
  4.  *
  5.  *    This source code is a complete THINK Class Library application for
  6.  *    use with Apple's MIDI Manager.
  7.  *
  8.  *    This program is freeware, although I retain all rights to it.  You
  9.  *    are free to distribute it provided you don't sell it.
  10.  *
  11.  *    If you have any comments about this program, I can be reached on
  12.  *    CompuServe at 70441,3055.
  13.  *
  14.  *
  15.  * Description:
  16.  *    This program has no user interface to speak of.  It's main purpose is
  17.  *    to illustrate the use of the CMIDI objects.  These objects are
  18.  *    described in the CMIDI Programmers Guide.
  19.  *
  20.  *    Like a left-handed guitar, Lefty makes your MIDI keyboard into a
  21.  *    “left-handed keyboard”.
  22.  *
  23.  *    It reverses the note numbers of Note On, Note Off, and Aftertouch
  24.  *    MIDI messages, i.e. the low notes are towards the right end of the
  25.  *    keyboard, while the high notes are towards the left.  All other
  26.  *    MIDI messages are simply passed straight through without change.
  27.  *
  28.  *    The note values are also calculated so that the black keys are
  29.  *    reversed correctly to complete the illusion.  Thus when filtering
  30.  *    through Lefty, middle C is found on the E key.  The graphic
  31.  *    in the “About Lefty” box illustrates this.
  32.  *
  33.  * Usage:
  34.  * ——————
  35.  *    You must have Apple's MIDI Manager (version 1.2 or later) software
  36.  *    to use this program.  In the simplest configuration, just route the
  37.  *    MIDI Manager output port through Lefty, and back into the input port.
  38.  *
  39.  *    You can also play back songs from a sequencer through Lefty for some
  40.  *    pretty unusual effects.
  41.  *
  42.  * Implementation Notes:
  43.  * —————————————————————
  44.  *    This source code is for use Symantec C++ 6.0 and the THINK Class Library.
  45.  *    Since this program is so simple, I have reduced the application to
  46.  *    this single source file.  Although Lefty doesn’t use most of TCL's
  47.  *    features, you still need to include them in the project.  As a
  48.  *    result, the application is about 110K+ in size which is “kind of large”.
  49.  *    Oh well, memory is cheap... 
  50.  *
  51.  *    If you really wish to use this program, and memory is an issue, you
  52.  *    should be able to rip out all the TCL files.  All you need is to
  53.  *    handle the menu events.
  54.  *—————————————————————————————————————————————————————————————————————
  55.  */
  56.  
  57. #include <CApplication.h>
  58. #include <Commands.h>
  59. #include <CBartender.h>
  60. #include <CDesktop.h>
  61. #include <CError.h>
  62.  
  63. #include "CMIDIClient.h"
  64. #include "CMIDIInputPort.h"
  65. #include "CMIDIOutputPort.h"
  66. #include "CMIDITimePort.h"
  67.  
  68. #define PORTNAMES            1024        // Resource ID for strings
  69. #define ABOUTBOX            1024        // An ALRT
  70.  
  71. #define FLIPNOTE            62            // Point at which to flip note numbers
  72.  
  73. #define INBUFSIZE            30000        // Nice big buffer
  74.  
  75. #define menuLEFTY            128            // MENU ID for DoCommand()
  76. #define cmdEnable            12801        // Only two commands needed
  77. #define cmdDisable            12802
  78.  
  79. #define    TIMEPORT            128
  80. #define INPORT                129
  81. #define OUTPORT                130
  82.  
  83. class CLeftyApp : public CApplication            // This is Lefty...
  84. {
  85.  
  86. public:
  87.  
  88.     CMIDIInputPort *    itsMIDIIn;
  89.     CMIDIOutputPort *    itsMIDIOut;
  90.     CMIDITimePort *        itsMIDITime;
  91.     
  92.     void                ILeftyApp(void);
  93.     void                UpdateMenus(void);    // Override a couple methods
  94.     void                DoCommand(long theCommand);
  95.     void                Exit(void);
  96.  
  97. private:
  98.  
  99.     static pascal short LeftyReadHook(MIDIPacket * ThePacketPtr, long TheRefCon);
  100.     static pascal short NormalReadHook(MIDIPacket * ThePacketPtr, long TheRefCon);
  101.     static pascal void    myConnectProc(short refnum, long refcon, short portType,
  102.                                          OSType clientID, OSType portID,
  103.                                          Boolean connect, short direction);
  104.  
  105.     static int                outRefNum;
  106.     static    unsigned char    newNote[128];        // Note translation table
  107.  
  108. };
  109.  
  110. // Referenced externals
  111.  
  112. extern    OSType               gSignature;
  113. extern    CMIDIClient        * gMIDIClient;
  114. extern    CApplication    * gApplication;
  115. extern    CBartender        * gBartender;
  116. extern    CError            * gError;
  117.  
  118. /*
  119.  * These variables are used locally.  Because we're running
  120.  * in interrupt context for the MIDI handler, we don't want
  121.  * to go through the overhead of putting these into objects,
  122.  * only to have to extract them for the handler.
  123.  */
  124.  
  125. unsigned char    CLeftyApp::newNote[128];
  126. int                CLeftyApp::outRefNum;        // For direct MIDI Manager Writes…
  127.  
  128. /*
  129.  *——— main ——————————————————————————————————————————————————————
  130.  */
  131. void main()
  132. {
  133.     gApplication = new(CLeftyApp);
  134.     ((CLeftyApp *)gApplication)->ILeftyApp();
  135.     gApplication->Run();
  136.     gApplication->Exit();
  137. }
  138.  
  139. /*
  140.  *——— LeftyReadHook ———————————————————————————————————————————————
  141.  * This is our application's MIDI Manager read function.  It 
  142.  * normally operates in interrupt context.
  143.  *
  144.  * This function looks to see whether the packet is a Note On,
  145.  * Note Off, or Poly After Pressure.  If so, it translates the 
  146.  * key number.  All other messages are passed straight through.
  147.  *
  148.  * NOTE: This read hook does not properly handle MIDI messages
  149.  *       which contain two or more Note On/Off messages (e.g.
  150.  *       80 66 7F 70 7F 75 7F ...).  If you need this, then you'll
  151.  *       just have to add it yourself.
  152.  *——————————————————————————————————————————————————————————————————
  153.  */
  154. pascal short CLeftyApp::LeftyReadHook(MIDIPacket * ThePacketPtr, long TheRefCon)
  155. {
  156.     long            SysA5 = SetA5(TheRefCon);
  157.     register unsigned char    theFlags;
  158.     register unsigned int    it;
  159.  
  160.     theFlags = ThePacketPtr->flags;
  161.     if (theFlags & midiMgrType)            // Ignore error packets.    
  162.     {
  163.         SetA5(SysA5);
  164.         return(midiMorePacket);            // Throw away        
  165.     }
  166.  
  167.     // Do we want to alter this packet?
  168.     
  169.     if (   (ThePacketPtr->len == 9)            // six byte header & three MIDI bytes 
  170.         && (ThePacketPtr->data[0] >= 0x80)    // is Note On, Note Off, or Key Pressure    
  171.         && (ThePacketPtr->data[0] <= 0xAF)
  172.         && ((theFlags & midiContMask) == midiNoCont) )    // Is a simple packet    
  173.     {
  174.         it = ThePacketPtr->data[1];        // translate the note byte    
  175.         ThePacketPtr->data[1] = CLeftyApp::newNote[it];    // using a lookup table        
  176.     }
  177.     // In many cases, it’s just as easy to call the MIDI Manager directly,
  178.     // as in this case of echoing each packet back out, assuming you 
  179.     // already know that the MIDI Manager is present and functioning.
  180.     //
  181.     // Presumably, because this code wouldn’t get executed at all unless
  182.     // that were the case, we can make this assumption.
  183.  
  184.      MIDIWritePacket(CLeftyApp::outRefNum, ThePacketPtr);
  185.     SetA5(SysA5);
  186.     return (midiMorePacket);            // Get next packet        
  187. }
  188.  
  189. /*
  190.  *——— NormalReadHook ———————————————————————————————————————————————
  191.  * This is the “Disabled” readHook.  It simply passes every packet
  192.  * straight through.
  193.  *——————————————————————————————————————————————————————————————————
  194.  */
  195. pascal short CLeftyApp::NormalReadHook(MIDIPacket * ThePacketPtr, long TheRefCon)
  196. {
  197.     long        SysA5 = SetA5(TheRefCon);
  198.  
  199.     if (! (ThePacketPtr->flags & midiMgrType) )    // Ignore error packets.
  200.     {
  201.          MIDIWritePacket(CLeftyApp::outRefNum, ThePacketPtr);
  202.     }
  203.     SetA5(SysA5);
  204.     return (midiMorePacket);            // Get next packet
  205. }
  206.  
  207.  
  208.  
  209. pascal void CLeftyApp::myConnectProc(short refnum, long refcon, short portType,
  210.      OSType clientID, OSType portID,
  211.      Boolean connect, short direction)
  212. {
  213.     portType &= midiPortTypeMask;
  214.     if (portType == midiPortTypeTime)
  215.     {
  216.         if (direction == midiInternalSync)
  217.         {
  218.             // There's stuff to be done here...
  219.         }
  220.         else
  221.         {
  222.             // but I ain't doin' it...
  223.         }
  224.     }
  225. }
  226.  
  227. /*
  228.  *——— ILeftyApp —————————————————————————————————————————————————
  229.  * Initialize the application.  Most of the work here is in
  230.  * creating and initializing the CMIDI objects.
  231.  *———————————————————————————————————————————————————————————————
  232.  */
  233. void CLeftyApp::ILeftyApp(void)
  234. {
  235.     register int     i;
  236.     Str255            theString;
  237.     OSErr            theResult;
  238.  
  239.     gSignature = 'Left';
  240.     
  241.     CApplication::IApplication(4, 20480L, 2048L, 2048L);
  242.     DoCommand(cmdAbout);
  243.  
  244.     gBartender->SetDimOption(menuLEFTY, dimNONE);
  245.     gBartender->SetUnchecking(menuLEFTY, TRUE);
  246.  
  247.     // Set up note mapping table.  By using a table, this program
  248.     // can be generalized for any kind of mappings.  Simply set the
  249.     // netNote[] array to the appropriate values.
  250.  
  251.     for (i = 0; i < 128; ++i)
  252.         CLeftyApp::newNote[i] = 0;                // just being sure
  253.  
  254.     for (i = 0; i < (2*FLIPNOTE) ; ++i)
  255.         CLeftyApp::newNote[i] = (2*FLIPNOTE) - i;
  256.  
  257.     // Sign into MIDI Manager.  Open input and output ports.
  258.     // Also open a time port, although we don't use it.
  259.  
  260.     gMIDIClient = new(CMIDIClient);
  261.     theResult = gMIDIClient->IMIDIClient(128);
  262.     if (! gError->CheckOSError(theResult))        // Can't go on...
  263.         DoCommand(cmdQuit);
  264.  
  265.                                                 // Time base
  266.     GetIndString(theString, PORTNAMES, 3);
  267.     itsMIDITime = new(CMIDITimePort);            // We don't actually use this time port    
  268.     itsMIDITime->IMIDITimePort(theString, 'ATim', TRUE, midiFormatMSec);        
  269.     itsMIDITime->LoadPatches('Port', TIMEPORT);
  270.     itsMIDITime->SetConnection((ProcPtr) CLeftyApp::myConnectProc);
  271.  
  272.                                                 // Output port
  273.     GetIndString(theString, PORTNAMES, 2);
  274.     itsMIDIOut = new(CMIDIOutputPort);
  275.     itsMIDIOut->IMIDIOutputPort(theString, 'Out ',
  276.              TRUE, itsMIDITime, midiGetCurrent);
  277.     itsMIDIOut->LoadPatches('Port', OUTPORT);    // Did we have any patches?    
  278.     CLeftyApp::outRefNum = itsMIDIOut->GetRefNum();    // Save for easy reference in our read hook
  279.  
  280.                                                 // Input port
  281.     GetIndString(theString, PORTNAMES, 1);
  282.     itsMIDIIn = new(CMIDIInputPort);
  283.     itsMIDIIn->IMIDIInputPort(theString, 'In  ', 
  284.             TRUE, itsMIDITime, midiGetEverything, INBUFSIZE, (ProcPtr) LeftyReadHook);
  285.     itsMIDIIn->LoadPatches('Port', INPORT);        // Did we have any patches?
  286.  
  287.     return;
  288. }
  289.  
  290. /*
  291.  *——— UpdateMenus —————————————————————————————————————————
  292.  */
  293. void CLeftyApp::UpdateMenus(void)
  294. {
  295.     register ProcPtr    theProc;
  296.     
  297.     inherited::UpdateMenus();
  298.     
  299.     gBartender->EnableCmd(cmdEnable);
  300.     gBartender->EnableCmd(cmdDisable);
  301.     
  302.     theProc = itsMIDIIn->GetReadHook();
  303.     
  304.     gBartender->CheckMarkCmd(cmdEnable, (theProc == (ProcPtr) LeftyReadHook));
  305.     gBartender->CheckMarkCmd(cmdDisable, (theProc == (ProcPtr) NormalReadHook));
  306. }
  307.  
  308. /*
  309.  *——— DoCommand ———————————————————————————————————————————
  310.  */
  311. void CLeftyApp::DoCommand(long theCommand)
  312. {
  313.     switch (theCommand)
  314.     {
  315.         case cmdEnable:
  316.             itsMIDIIn->SetReadHook((ProcPtr) LeftyReadHook);
  317.             break;
  318.         case cmdDisable:
  319.             itsMIDIIn->SetReadHook((ProcPtr) NormalReadHook);
  320.             break;
  321.         case cmdAbout:
  322.             PositionDialog('ALRT', ABOUTBOX);    
  323.             Alert(ABOUTBOX, FALSE);
  324.             break;
  325.         default:
  326.             inherited::DoCommand(theCommand);
  327.             break;
  328.     }
  329. }
  330.  
  331. /*
  332.  *——— Exit ———————————————————————————————————————————————————————
  333.  * Log out of the MIDI Manager, and quit.
  334.  *————————————————————————————————————————————————————————————————
  335.  */
  336. void CLeftyApp::Exit()
  337. {    
  338.     itsMIDIIn->SavePatches('Port', INPORT);
  339.     itsMIDIOut->SavePatches('Port', OUTPORT);
  340.     itsMIDITime->SavePatches('Port', TIMEPORT);
  341.  
  342.     itsMIDIIn->Dispose();
  343.     itsMIDIOut->Dispose();
  344.     itsMIDITime->Dispose();
  345.  
  346.     gMIDIClient->Dispose();        // Sign out of MIDI Manager
  347.      
  348.     ExitToShell();
  349. }
  350.  
  351. // end of Lefty.cp
  352.